package com.yumiusik.util;

import it.sauronsoftware.jave.AudioAttributes;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.EncodingAttributes;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Base64Utils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import pe.com.trestperu.common.exception.CoreServiceException;
import pe.com.trestperu.common.listener.ContextLoaderListener;
import pe.com.trestperu.common.service.GenericService;
import pe.com.trestperu.common.util.FlvAudioStream;
import pe.com.trestperu.common.util.OrderedMap;
import pe.com.trestperu.common.util.SendMail;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yumiusik.common.domain.AudioInformation;
import com.yumiusik.common.domain.PlayerErrorRegister;
import com.yumiusik.common.domain.PlayerLogShare;
import com.yumiusik.common.domain.PlayerLogging;
import com.yumiusik.status.YumiusikStatus;
import com.yumiusik.status.YumiusikStatusHolder;

public class YumiusikUtils {
	
	static final Log log = LogFactory.getLog(YumiusikUtils.class);

	static Map<String, Map<String, Integer>> audioFormat;

	static final String NO = "NO";

	static final String SI = "SI";

	static final int ITERATION_NUMBER = 1000;

	static final String PHASE_ENCRYPTION = "YumiusikPlayer";

	public enum STATUS_HOLDER {
		REGISTER, IN_PROCESS, DOWNLOADING, CONVERTING, ENDED
	}

	private static GenericService getGenericService(){
		return (GenericService)ContextLoaderListener.getBean(GenericService.class);
	}

	private static Map<String, Integer> getAudioFormat(String format){
		if(audioFormat == null){
			Map<String, Integer> high = new HashMap<String, Integer>();
			high.put("bitRate", new Integer(128000));
			high.put("channels", new Integer(2));
			high.put("samplingRate", new Integer(44100));

			Map<String, Integer> mid = new HashMap<String, Integer>();
			mid.put("bitRate", new Integer(64000));
			mid.put("channels", new Integer(2));
			mid.put("samplingRate", new Integer(22050));

			Map<String, Integer> low = new HashMap<String, Integer>();
			mid.put("bitRate", new Integer(32000));
			mid.put("channels", new Integer(1));
			mid.put("samplingRate", new Integer(22050));

			audioFormat = new HashMap<String, Map<String, Integer>>();
			audioFormat.put("high", high);
			audioFormat.put("mid", mid);
			audioFormat.put("low", low);
		}
		return audioFormat.get(format);
	}

	public static AudioInformation createAudioInformation(String video_id) throws IOException, SAXException, ParserConfigurationException, CoreServiceException{
		URL url = new URL(YumiusikConstants.YOUTUBE_GET_DATA_URL + "/" + video_id);
		HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
		cnx.setDoInput(true);
		cnx.setDoOutput(true);
		cnx.setRequestMethod("GET");

		AudioInformation audioInformation = (AudioInformation)ContextLoaderListener.getBean(AudioInformation.class);
		audioInformation.setVideo_id(video_id);

		// Leyendo la respuesta
		InputStream in = cnx.getInputStream();
		Document doc = readXml(in);
		NodeList nodeList = doc.getElementsByTagName("yt:duration");
		Node node = nodeList.item(0);

		audioInformation.setDuration(new Long(node.getAttributes().getNamedItem("seconds").getTextContent()));
		nodeList = doc.getElementsByTagName("media:thumbnail");
		node = nodeList.item(0);
		audioInformation.setImage(node.getAttributes().getNamedItem("url").getTextContent());

		nodeList = doc.getElementsByTagName("media:title");
		node = nodeList.item(0);
		audioInformation.setTitle(node.getTextContent());

		in.close();
		
		audioInformation.setLink(YumiusikConstants.YOUTUBE_GENERAL_URL+video_id);
		audioInformation.setLength(new Long(-1));
		audioInformation.setValidate("NO");
		getGenericService().save(audioInformation);

		return audioInformation;
	}

	public static String toXML(Map<String, Object> map, String root) {
		StringBuilder sb = new StringBuilder("<");
		sb.append(root);
		sb.append(">");

		for (Map.Entry<String, Object> e : map.entrySet()) {
			sb.append("<");
			sb.append(e.getKey());
			sb.append(">");

			sb.append(e.getValue());

			sb.append("</");
			sb.append(e.getKey());
			sb.append(">");
		}

		sb.append("</");
		sb.append(root);
		sb.append(">");

		return sb.toString();
	}

	public static Document readXml(InputStream is) throws SAXException, IOException, ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

		dbf.setValidating(false);
		dbf.setIgnoringComments(false);
		dbf.setIgnoringElementContentWhitespace(true);
		dbf.setNamespaceAware(true);
		// dbf.setCoalescing(true);
		// dbf.setExpandEntityReferences(true);

		DocumentBuilder db = null;
		db = dbf.newDocumentBuilder();
		db.setEntityResolver(new NullResolver());

		return db.parse(is);
	}

	public static String getVideoIdFromURL(String urlYouTube) throws MalformedURLException{
		URL url = new URL(urlYouTube);
		String[] query = StringUtils.split(url.getQuery(), "&");
		for (int i = 0; i < query.length; i++) {
			if (query[i].startsWith("v")) {
				String[] part = StringUtils.split(query[i], "=");
				return part[1];
			}
		}
		return null;
	}

	public static File existsMP3(String video_id, String user_agent) throws IOException{
		File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
		if(!mp3.exists()){
			mp3 = downloadMP3(video_id, user_agent);
			if(mp3 == null){
				sendEmail(video_id);
			}
		}
		return mp3;
	}

	public static File returnMP3File(String video_id, String type, String user_agent) throws IOException{
		String status = YumiusikUtils.getStatusHolder(video_id);
		String process = "convertingMP3File";
		File returnmp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_"+type+"_youtube"+File.separatorChar+video_id+".mp3");
		if(!returnmp3.exists()){
			File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
			if(!mp3.exists()){
				mp3 = existsMP3(video_id, user_agent);
			}
			if(mp3 != null){
				if(StringUtils.isEmpty(status)){
					YumiusikUtils.registerStatusHolder(video_id, process, YumiusikUtils.STATUS_HOLDER.IN_PROCESS);
					AudioAttributes audio = new AudioAttributes();
					audio.setCodec("libmp3lame");
					Map<String, Integer> audioConfig = getAudioFormat(type);
					audio.setBitRate(audioConfig.get("bitRate"));
					audio.setChannels(audioConfig.get("channels"));
					audio.setSamplingRate(audioConfig.get("samplingRate"));
					EncodingAttributes attrs = new EncodingAttributes();
					attrs.setFormat("mp3");
					attrs.setAudioAttributes(audio);
					Encoder encoder = new Encoder();
					PlayerErrorRegister errorRegister = null;
					try{
						errorRegister = (PlayerErrorRegister)getGenericService().findById(PlayerErrorRegister.class, video_id);
						encoder.encode(mp3, returnmp3, attrs);
						YumiusikUtils.registerStatusHolder(video_id, process, YumiusikUtils.STATUS_HOLDER.ENDED);
						if(errorRegister != null){
							getGenericService().deleteById(PlayerErrorRegister.class, errorRegister.getVideo_id());
						}
					}catch(Exception e){
						if(returnmp3.exists()){
							returnmp3.delete();
						}
						try{
							if(errorRegister != null){
								if(errorRegister.getCounter() == 10 && NO.equals(errorRegister.getFlag())){
									errorRegister.setFlag(SI);
									try{
										sendErrorEmail(e);
									}catch(Exception e1){
										log.error(e1);
									}
									log.error(e);
								}else{
									errorRegister.setCounter(errorRegister.getCounter() + 1);
								}
							}else{
								errorRegister = (PlayerErrorRegister)ContextLoaderListener.getBean(PlayerErrorRegister.class);
								errorRegister.setVideo_id(video_id);
								errorRegister.setFlag(NO);
								errorRegister.setCounter(Long.valueOf(1));
							}
							getGenericService().save(errorRegister);
						}catch(Exception ex){
							log.error("errorRegister :: ",ex);
						}
					}
				}
			}else{
				returnmp3 = null;
			}

		}
		return returnmp3;
	}

	public static String convertToString(List<Map<String, String>> songList){
		String result = "<ul>";
		for(Map<String, String> song : songList){
			result += "<li>";
			result += MapUtils.getString(song, "title");
			result += "</li>";
		}
		result += "</ul>";
		return result;
	}

	public static void saveLog(HttpServletRequest request, String typeAccess, String quality, String video_id) throws Exception {
		String ip_remote = request.getHeader("x-forwarded-for") != null ? request.getHeader("x-forwarded-for") : request.getRemoteHost();
		PlayerLogging logging = (PlayerLogging)ContextLoaderListener.getBean(PlayerLogging.class);
		logging.setUser_agent(StringUtils.isNotEmpty(request.getHeader("User-Agent")) ? request.getHeader("User-Agent") : "-- NO USER AGENT --");
		logging.setIp_address(ip_remote);
		String countryCode = "";
		try{
			countryCode = getCountryCodeFromIP(ip_remote);
		}catch(Exception e){
			log.warn("saveLog.countryCode :: ",e);
			countryCode = "PE";
		}
		logging.setCountry_code(countryCode);
		logging.setType_log(typeAccess);
		logging.setQuality(quality);
		logging.setVideo_id(video_id);
		if(YumiusikConstants.TYPE_ACCESS.GetInformation.equals(typeAccess)){
			// Do something special for this kind of access
		}
		if(YumiusikConstants.TYPE_ACCESS.Search.equals(typeAccess)){
			// fill vq and page_number
			Map<String, String> params = convertQStringToMap(request.getQueryString());
			logging.setVq(params.get("vq"));
			logging.setPage_number(params.get("start-index"));
		}
		if(YumiusikConstants.TYPE_ACCESS.Verify.equals(typeAccess)){
			// Do something special for this verification
		}
		if(YumiusikConstants.TYPE_ACCESS.Play.equals(typeAccess)){
			// Do something special for this playing
		}
		if(StringUtils.isNotEmpty(logging.getIp_address()))
			getGenericService().save(logging);
	}

	public static PlayerLogShare saveLogShare(HttpServletRequest request, Map<String, String> data, String sended) throws Exception {
		PlayerLogShare logging = (PlayerLogShare)ContextLoaderListener.getBean(PlayerLogShare.class);
		String userFrom = MapUtils.getString(data, "mailFrom");
		String mailTo = MapUtils.getString(data, "mailTo");
		String songID = MapUtils.getString(data, "songID", null);
		String playListID = MapUtils.getString(data, "playListID", null);
		String message = MapUtils.getString(data, "message", null);
		logging.setUserFrom(userFrom);
		logging.setMailTo(mailTo);
		logging.setSongID(songID);
		logging.setPlayListID(playListID);
		logging.setMessage(message);
		logging.setSended(sended);
		logging.setListened(YumiusikConstants.SI_NO.NO);
		logging.setHide(YumiusikConstants.SI_NO.NO);
		getGenericService().save(logging);
		return logging;
	}
	private static String getCountryCodeFromIP(String ip) throws Exception{
		URL url = new URL(YumiusikConstants.SERVICE_COUNTRY_CONVERT + ip);	
		HttpURLConnection cnx = (HttpURLConnection) url.openConnection();
		cnx.setDoInput(true);
		cnx.setDoOutput(true);
		cnx.setRequestMethod("GET");

		InputStream in = cnx.getInputStream();
		StringBuffer sb = new StringBuffer();
		int c;
		while ((c = in.read()) != -1) sb.append((char) c);	
		in.close();

		ObjectMapper mapper = new ObjectMapper();
		Map<?, ?> map = mapper.readValue(sb.toString(), HashMap.class);
		@SuppressWarnings("unchecked")
		Map<String, Object> _c = MapUtils.getMap(map, "c");
		return MapUtils.getString(_c, ip);
	}

	public static Map<String, String> convertQStringToMap(String qstring){
		Map<String, String> result = new HashMap<String, String>();
		String[] first = StringUtils.split(qstring, "&");
		for(String item : first){
			String[] second = StringUtils.split(item, "=");
			result.put(second[0], second[1]);
		}
		return result;
	}

	public static File downloadMP3(String video_id, String user_agent) throws IOException{
		File mp3 = new File(SystemUtils.getUserHome().getAbsolutePath()+File.separatorChar+"mp3_youtube"+File.separatorChar+video_id+".mp3");
		HttpURLConnection cnx = null;
		try{
			String parameters = MessageFormat.format(YumiusikConstants.MP3_DOWNLOAD_INFO_URL_QUERY, video_id);
			URL url = new URL(YumiusikConstants.MP3_DOWNLOAD_INFO_URL + "?" + parameters);
			// http://www.youtube-mp3.org/a/itemInfo/?video_id=Gx1Z2AfFnio&ac=www&t=grp&r=1381734470826&s=104643
			String htmlResult = getStringFromURL(url, user_agent, StandardCharsets.UTF_8.name());
			ObjectMapper mapper = new ObjectMapper();
			@SuppressWarnings("unchecked")
			Map<String, Object> map = mapper.readValue(StringUtils.substring(htmlResult, htmlResult.indexOf("{"), htmlResult.length()), HashMap.class);

			// Descargando
			parameters = MessageFormat.format(YumiusikConstants.MP3_DOWNLOAD_URL_QUERY, new Object[]{video_id, MapUtils.getString(map, "h")});
			url = new URL(YumiusikConstants.MP3_DOWNLOAD_URL + "?" + parameters);
			// http://www.youtube-mp3.org/get?ab=128&video_id=Gx1Z2AfFnio&h=e1c858171845c62c419ff47657571be8&r=1414426166864.1460143701&s=19640
			cnx = (HttpURLConnection) url.openConnection();
			cnx.setRequestMethod("GET");

			InputStream inputStream = cnx.getInputStream();
			FileUtils.copyInputStreamToFile(inputStream, mp3);
			inputStream.close();
			cnx.disconnect();
		}catch(Exception ex){
			try{
				while(!mp3.exists()){
					downloadMP4(video_id, mp3, user_agent);
				}
			}catch(Exception e){
				log.error("downloadMP3.downloadMP4 :: ",e);
			}
		}finally{
			if(cnx != null){
				cnx.disconnect();
				cnx = null;
			}
		}

		if(mp3.exists()){
			return mp3;
		}else{
			return null;
		}
	}

	private static void downloadMP4(String video_id, File mp3, String user_agent) throws Exception{

		File directorio = mp3.getParentFile();
		if(!directorio.exists()){
			directorio.mkdirs();
		}
		String[] lstFLV = getFLVLink(video_id, user_agent);
		if(lstFLV != null){
			for(String flv_url : lstFLV){
				if(log.isDebugEnabled()){
					log.info("flv_url :: " + flv_url);
				}
				URL url = new URL(flv_url);
				File tmpFile = null;
				try{
					tmpFile = new File(FileUtils.getTempDirectory().getAbsolutePath()+"/"+video_id+".mp4");
					if(log.isDebugEnabled()){
						log.info("tmpFile :: "+tmpFile.getAbsolutePath());
					}
					try{
						if(log.isDebugEnabled()){
							log.info("try to downloaded :: " + tmpFile.getName());
						}
						FileUtils.copyURLToFile(url, tmpFile);
						log.info("Downloaded :: " + tmpFile.getName());
					}catch(Exception e){
						throw new Exception("Can't download from URL :: " + url.toExternalForm());
					}

					if(log.isDebugEnabled()){
						log.info("Start configuration to extract audio");
					}
					AudioAttributes audio = new AudioAttributes();
					audio.setCodec("libmp3lame");
					Map<String, Integer> audioConfig = getAudioFormat("high");
					audio.setBitRate(audioConfig.get("bitRate"));
					audio.setChannels(audioConfig.get("channels"));
					audio.setSamplingRate(audioConfig.get("samplingRate"));
					EncodingAttributes attrs = new EncodingAttributes();
					attrs.setFormat("mp3");
					attrs.setAudioAttributes(audio);
					Encoder encoder = new Encoder();

					try{
						if(log.isDebugEnabled()){
							log.info("Ready to extract audio");
						}
						encoder.encode(tmpFile, mp3, attrs);
						log.info("Audio extracted [" + tmpFile.getName() + "] sucessfully");
						tmpFile.delete();
						log.info("Deleting temp file [" + tmpFile.getName() + "]");
						break;
					}catch(Exception e){
						FlvAudioStream audioStream = null;
						try{
							if(log.isDebugEnabled()){
								log.info("Problem happen try with second method to extract audio");
							}
							audioStream = new FlvAudioStream(new FileInputStream(tmpFile));
							FileUtils.copyInputStreamToFile(audioStream, mp3);
							if(log.isDebugEnabled()){
								log.info("Audio extracted sucessfully");
								log.info("Deleting temp file");
							}
							tmpFile.delete();
							break;
						}catch(Exception x){
							if(mp3.exists()){
								mp3.delete();
							}
							if(log.isDebugEnabled()){
								log.error("FlvAudioStream :: ", x);
							}
						}
						if(log.isDebugEnabled()){
							log.error("encoder.encode :: ", e);
						}
					}
				}catch(Exception e){
					if(mp3.exists()){
						mp3.delete();
					}
					if(log.isDebugEnabled()){
						log.error("audio :: ",e);
					}
				}finally{
					if(tmpFile != null){
						tmpFile.delete();
					}
				}
			}

		}else{
			if(mp3.exists()){
				mp3.delete();
			}
		}
	}

	private static String[] getFLVLink(String video_id, String user_agent){
		String flv_url = YumiusikConstants.YOUTUBE_GET_VIDEO_INFO + "?video_id=" + video_id+"&asv=3&el=detailpage&hl=en_US&sts=1588";
		HttpURLConnection cnx = null;
		List<String> lstLinks = new ArrayList<String>();
		try{
			URL url = new URL(flv_url);
			flv_url = getStringFromURL(url, user_agent, StandardCharsets.UTF_8.name());
			String[] title_search = StringUtils.split(flv_url, "&");
			String title = StringUtils.EMPTY;
			for(int i = 0; i < title_search.length; i++){
				String line = title_search[i];
				if(line.startsWith("title")){
					String[] _parts = line.split("=");
					if(_parts.length == 2){
						title = StringUtils.split(line,"=")[1];
						break;
					}
				}
			}
			String[] urls = ("&"+URLDecoder.decode(flv_url, StandardCharsets.UTF_8.name())).split("&url=http%3A%2F%2F");
			List<String> select_url = new ArrayList<String>();
			for(int i = 0; i < urls.length; i++){
				String[] __url = URLDecoder.decode(("http://"+urls[i]), StandardCharsets.UTF_8.name()).split("url=");
				for(String _url : __url){
					_url = URLDecoder.decode(_url, StandardCharsets.UTF_8.name());
					if(_url.indexOf("type=video/mp4") > -1){
						select_url.add(_url);
					}
				}
			}
			if(CollectionUtils.isNotEmpty(select_url)){
				for(String _select_url : select_url){
					OrderedMap query_params = new OrderedMap();
					URL nurl = new URL(_select_url);
					String query = nurl.getQuery();
					if(query != null){
						String[] params = StringUtils.split(query, "&");
						
						for(int i = 0; i < params.length; i++){
							String param = params[i];
							if(StringUtils.isNotBlank(param)){
								String[] _params = param.split("=");
								if(_params.length == 2){
									String key = _params[0];
									String value = URLDecoder.decode(_params[1], StandardCharsets.UTF_8.name());
									query_params.put(key, value);
								}
							}
						}
						query_params.put("title", StringUtils.replace(title, "+", "%20"));
						String link = StringUtils.remove(_select_url, nurl.getQuery());
						lstLinks.add(link.substring(0, link.indexOf("?")) + "?" + urlEncode(query_params));
					}
				}
				return lstLinks.toArray(new String[lstLinks.size()]);
			}else{
				return null;
			}
		}catch(Exception e){
			log.error("getFLVLink :: ",e);
		}finally{
			if(cnx != null){
				cnx.disconnect();
			}
		}
		return null;
	}

	private static String getStringFromURL(URL url, String user_agent, String content_encoding){
		HttpURLConnection cnx = null;
		StringWriter writer = new StringWriter();
		try{
			cnx = (HttpURLConnection) url.openConnection();
			cnx.setRequestMethod("GET");
			cnx.setRequestProperty("User-Agent", user_agent);
			cnx.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			cnx.setUseCaches(false);
			cnx.setDoInput(true);
			cnx.setDoOutput(true);
			IOUtils.copy(cnx.getInputStream(), writer, content_encoding);
		}catch(Exception e){
			log.error("getStringFromURL :: ",e);
		}finally{
			if(cnx != null){
				cnx.disconnect();
			}
		}
		return writer.toString();
	}

	public static void sendEmail(String video_id){
		SendMail sendMail = (SendMail)ContextLoaderListener.getBean(SendMail.class);
		sendMail.setTextHtml(true);
		sendMail.setSubject("YUMIUSIK [ERROR] - Verify MP3 " + video_id);
		//sendMail.setText("Next video have problems to be downloaded :: " + video_id + "<br>Please click next URL :: http://www.youtube-mp3.org/#v=" + video_id);
		sendMail.setText("The following video have problems downloading :: " + video_id + "<br>Please click next URL :: <a href=\""+YumiusikConstants.MP3_DOWNLOAD_SNIPMP3_SERVICE+video_id+"\" target=\"_blank\">Download " + video_id + "</a>");
		sendMail.setFrom(YumiusikConstants.MAIL_USER_FROM);
		sendMail.setTo(YumiusikConstants.MAIL_USER_TO);
		try {
			sendMail.sendMail();
		} catch (Exception e) {
			log.error(e);
			sendMail = (SendMail)ContextLoaderListener.getBean("SendMailBackup");
			sendMail.setTextHtml(true);
			sendMail.setSubject("YUMIUSIK [ERROR] - Verify MP3 " + video_id);
			sendMail.setText("The following video have problems downloading :: " + video_id + "<br>Please click next URL :: <a href=\""+YumiusikConstants.MP3_DOWNLOAD_SNIPMP3_SERVICE+video_id+"\" target=\"_blank\">Download " + video_id + "</a>");
			sendMail.setFrom(YumiusikConstants.MAIL_USER_FROM);
			sendMail.setTo(YumiusikConstants.MAIL_USER_TO);
			try{
				sendMail.sendMail();
			}catch(Exception ex){
				log.error("sendEmail :: ",ex);
			}
		}
	}

	public static void sendShareEmail(String userFrom, String userTo, String subject, String message) throws Exception{
		SendMail sendMail = (SendMail)ContextLoaderListener.getBean(SendMail.class);
		sendMail.setTextHtml(true);
		sendMail.setFrom(userFrom);
		sendMail.setTo(userTo);
		try {
			sendMail.setSubject(subject);
			sendMail.setText(message);
			sendMail.sendMail();
		} catch (Exception e) {
			log.error("sendShareEmail :: ",e);
			sendMail = (SendMail)ContextLoaderListener.getBean("SendMailBackup");
			sendMail.setTextHtml(true);
			sendMail.setFrom(userFrom);
			sendMail.setTo(userTo);
			sendMail.setSubject(subject);
			sendMail.setText(message);
			sendMail.sendMail();
			try{
				sendMail.sendMail();
			}catch(Exception ex){
				log.error("sendShareEmail.SendMailBackup ::",ex);
				throw ex;
			}
		}
	}

	private static SecretKeySpec getKey() throws Exception{
		MessageDigest digest = MessageDigest.getInstance("SHA");
		digest.update(PHASE_ENCRYPTION.getBytes());
		SecretKeySpec key = new SecretKeySpec(digest.digest(), 0, 16, "AES");
		return key;
	}

	public static String encrypt(String string) throws Exception{
		Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
		aes.init(Cipher.ENCRYPT_MODE, getKey());
		byte[] encrypt = aes.doFinal(string.getBytes("UTF-8"));
		return new String(Base64Utils.encode(encrypt),"UTF-8");
	}

	public static String decrypt(String string) throws Exception{
		Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
		aes.init(Cipher.DECRYPT_MODE, getKey());
		byte[] encrypt = aes.doFinal(Base64Utils.decodeFromString(string));
		return new String(encrypt, "UTF-8");
	}

	private static void sendErrorEmail(Exception error){
		String errorText = exceptionToText(error);
		SendMail sendMail = (SendMail)ContextLoaderListener.getBean(SendMail.class);
		sendMail.setTextHtml(true);
		sendMail.setSubject("YUMIUSIK [ERROR] - General Error");
		sendMail.setText("The System have the following general error <br>" + errorText);
		sendMail.setFrom(YumiusikConstants.MAIL_USER_FROM);
		sendMail.setTo(YumiusikConstants.MAIL_USER_TO);
		try {
			sendMail.sendMail();
		} catch (Exception e) {
			log.error("sendErrorEmail :: ",e);
		}
	}

	private static String exceptionToText(Exception error){
		String result = StringUtils.EMPTY;
		StackTraceElement[] element = error.getStackTrace();
		for(int i = 0; i < element.length; i++){
			result += element[i].toString();
		}
		return result;
	}

	private static String urlEncode(OrderedMap map) {
        StringBuilder sb = new StringBuilder();
        Iterator<Object> keys = map.keySet().iterator();
        while(keys.hasNext()){
        	if(sb.length() > 0){
        		sb.append("&");
        	}
        	String parameter = (String)keys.next();
        	String value = (String)map.get(parameter);
        	sb.append(String.format("%s=%s",parameter,value));
        }
        return sb.toString();
    }

	public static void registerStatusHolder(String video_id, String process, STATUS_HOLDER status){
		if(status != STATUS_HOLDER.ENDED){
			YumiusikStatusHolder yumiusikStatusHolder = (YumiusikStatusHolder)ContextLoaderListener.getBean(YumiusikStatusHolder.class);
			YumiusikStatus yumiusikStatus = (YumiusikStatus)ContextLoaderListener.getBean(YumiusikStatus.class);
			yumiusikStatus.setVideoId(video_id);
			yumiusikStatus.setStatus(status.name());
			yumiusikStatus.setProcess(process);
			yumiusikStatusHolder.put(video_id, yumiusikStatus);			
		}else{
			removeStatusHolder(video_id);
		}
	}

	public static void removeStatusHolder(String video_id){
		YumiusikStatusHolder yumiusikStatusHolder = (YumiusikStatusHolder)ContextLoaderListener.getBean(YumiusikStatusHolder.class);
		yumiusikStatusHolder.remove(video_id);		
	}
	
	public static void removeStatusHolder(AudioInformation audioInformation){
		YumiusikStatusHolder yumiusikStatusHolder = (YumiusikStatusHolder)ContextLoaderListener.getBean(YumiusikStatusHolder.class);
		yumiusikStatusHolder.remove(audioInformation.getVideo_id());
	}

	public static String getStatusHolder(String video_id){
		YumiusikStatusHolder yumiusikStatusHolder = (YumiusikStatusHolder)ContextLoaderListener.getBean(YumiusikStatusHolder.class);
		return yumiusikStatusHolder.getStatus(video_id);
	}
}
class NullResolver implements EntityResolver {
	public InputSource resolveEntity(String publicId, String systemId)
			throws SAXException, IOException {
		return new InputSource(new StringReader(StringUtils.EMPTY));
	}
}